#pragma once

#include <AVUIDependencyObject.h>
#include <AVUIMatrix.h>

namespace AVUI {

class Brush;
class RenderData;
class GeometryBuilder;
class Renderer;
class Transform;
class Viewport2DVisual3D;
class Geometry;
class BitmapEffect;
class BitmapEffectInput;
class DoubleCollection;
class DrawingGroup;
class GeneralTransform;
class GeneralTransform2DTo3D;
class HitTestResult;
class HitTestParameters;
class PointHitTestParameters;
class GeometryHitTestResult;
class GeometryHitTestParameters;
class Effect;

class HitTestFilterBehavior
{
public:
    enum Enum
    {
        ContinueSkipSelfAndChildren = 0,
        ContinueSkipChildren = 2,
        ContinueSkipSelf = 4,
        Continue = 6,
        Stop = 8
    };
};

class HitTestResultBehavior
{
public:
    enum Enum
    {
        Stop,
        Continue
    };
};


class HitTestResultArgs : public Object
{
public:
    DECLARE_ELEMENT(HitTestResultArgs, Object);

    HitTestResultBehavior::Enum ResultBehavior;    
};

class HitTestFilterArgs : public Object
{
public:
    DECLARE_ELEMENT(HitTestFilterArgs, Object);
    HitTestFilterArgs() : FilterBehavior(HitTestFilterBehavior::Continue) { }

    HitTestFilterBehavior::Enum FilterBehavior;
};

typedef AVUI::TDelegate<HitTestResultArgs> HitTestResultCallback;
typedef AVUI::TDelegate<HitTestFilterArgs> HitTestFilterCallback;

class TopMostHitResult
{
public:

    // Methods
    TopMostHitResult() { }

    void HitTestResultCheck(Object* pResult, HitTestResultArgs* pArgs)
    {
        m_pHitResult = (HitTestResult*) pResult;
        pArgs->ResultBehavior = HitTestResultBehavior::Stop;
    }

    void NoNested2DFilter(Object* pPotentialHitTestTarget, HitTestFilterArgs* pArgs)
    {
        if(is_type<Viewport2DVisual3D>(pPotentialHitTestTarget))
        {
            pArgs->FilterBehavior = HitTestFilterBehavior::ContinueSkipChildren;            
        }
        else
        {
            pArgs->FilterBehavior = HitTestFilterBehavior::Continue;
        }
    }

    HitTestResult* get_Result() { return m_pHitResult; }

private:

    TRefCountedPtr<HitTestResult> m_pHitResult;
};

class AncestorChangedEventArgs : public Object
{
public:
    DECLARE_ELEMENT(AncestorChangedEventArgs, Object);

    AncestorChangedEventArgs(DependencyObject* pSubRoot, DependencyObject* pOldParent) { }

    DependencyObject* get_Ancestor() { return m_pSubRoot; }
    DependencyObject* get_OldParent() { return m_pOldParent; }

private:
    DependencyObject* m_pOldParent;
    DependencyObject* m_pSubRoot;
};
DEFINE_EVENTHANDLER(AncestorChanged);


// Should this just be a bitfield struct?
class VisualFlags
{
public:
    enum Enum
    {
        None =               0x0,
        HasVisualTransform = 0x1,
        HasVisualClip  =     0x2,
        NodeHasEffect  =     0x4,
        HasVisualOpacity = 0x8,
        IsGeometryValid =       0x10,
        IsOpacitySuppressed =  0x20,
        IsCollapsed =       0x40,
        IsViewport3DVisual =      0x80,
        HasOpacityMask    = 0x100,
        IsMediaElement    = 0x200,
        IsVisualBrushRoot = 0x400,
        IsLayoutSuspended= 0x800,
        IsAttachedToPresentationSource = 0x1000,
        IsLayoutIslandRoot = 0x2000,
        IsSubtreeDirtyForPrecompute = 0x4000,
        IsRenderValid = 0x8000,
        HasChildren   = 0x10000,
        IsVisualChildrenIterationInProgress = 0x20000,

        NodeInSubtreeUsesRealizationCaches =  0x40000,
        NodeUsesRealizationCaches =           0x80000,
        SubTreeHoldsAncestorChanged =        0x100000,
        FindCommonAncestor =                 0x200000,
        Are3DContentBoundsValid =            0x400000,
        RegisteredForAncestorChanged =         0x800000,
        NodeOrDescendantIntroducesGraphness = 0x1000000,
        NodeHasBitmapEffect = 0x2000000,
        NodeRequiresNewRealization = 0x4000000,
        NodeInSubtreeRequiresNewRealization = 0x8000000,
        NodeNeedsBitmapEffectUpdate =        0x10000000,
        IsUIElement =        0x20000000,
        ReentrancyFlag =        0x40000000,
    };
};

class Visual : public DependencyObject
{
    friend class Viewport3DVisual;
    friend class Visual3D;
    friend class VisualTreeHelper;
    friend class VisualDrawingContext;
    friend class VisualBrush;
    friend class UIElement;
    friend class Window;
    friend class PopupRoot;
    friend class Application;
    friend class WindowManager;
    friend class UIElementCollection;
    friend class XamlParser;
    friend class FrameworkElement;
    friend class Viewport2DVisual3D;
    friend class GeneralTransform2DTo3DTo2D;
    friend class ContextLayoutManager;
    template<class T>
        friend class DescendentsWalker;

public:
    Visual();
    virtual ~Visual();

    DECLARE_ELEMENT(Visual, DependencyObject);

    virtual int get_VisualChildrenCount() { return 0; }
    virtual Visual* GetVisualChild(int) { LibraryCriticalError(); return NULL; };

    void BuildGeometry(GeometryBuilder* pGeometryBuilder);

    void RenderDependencies(Renderer* pRenderer, const Matrix& parentMatrix, const Rect& clipRect);

    void Render(Renderer* pRenderer, const Matrix& parentMatrix, const Rect& clipRect);
    void RenderCore(Renderer* pRenderer, const Matrix& localMatrix, Rect localClipRect);


    DependencyObject* get_InternalVisualParent() { return m_pVisualParent; }
    DependencyObject* get_VisualParent() { return get_InternalVisualParent(); }

    const Vector& get_VisualOffset() const { return m_offset; }
    void set_VisualOffset(const Vector& offset);
    void InternalSetOffsetWorkaround(const Vector& offset) { set_VisualOffset(offset); }

    Transform* get_VisualTransform();
    void set_VisualTransform(Transform* pTransform);
    void InternalSetTransformWorkaround(Transform* pTransform) { set_VisualTransform(pTransform); }

    float get_VisualOpacity();
    void set_VisualOpacity(float opacity);

    Geometry* get_VisualClip();
    void set_VisualClip(Geometry* pVisualClip) { ChangeVisualClip(pVisualClip, false); }
    Rect get_VisualClipBounds();

    Brush* get_VisualOpacityMask();
    void set_VisualOpacityMask(Brush* pOpacityMask);

    Effect* get_VisualEffect();
    void set_VisualEffect(Effect* pEffect);

    void set_HasVisualClip(bool hasVisualClip) { WriteFlag(VisualFlags::HasVisualClip, hasVisualClip); }
    bool get_HasVisualClip() const { return ReadFlag(VisualFlags::HasVisualClip); }

    void set_IsViewport3DVisual(bool isViewport3DVisual) { WriteFlag(VisualFlags::IsViewport3DVisual, isViewport3DVisual); }
    bool get_IsViewport3DVisual() const { return ReadFlag(VisualFlags::IsViewport3DVisual); }

    void set_IsMediaElement(bool isMediaElement) { WriteFlag(VisualFlags::IsMediaElement, isMediaElement); }
    bool get_IsMediaElement() const { return ReadFlag(VisualFlags::IsMediaElement); }

    Rect get_SubgraphBounds() const { return m_bboxSubgraph; };
    Rect get_VisualDescendantBounds() { return m_bboxSubgraph; }

    void ClearVisualClip();
    DrawingGroup* GetDrawing() { return NULL; }

    EdgeMode::Enum get_VisualEdgeMode() { return EdgeMode::Unspecified; }

    DependencyObject* FindCommonVisualAncestor(DependencyObject* pOtherVisual) { return NULL; }

    virtual TRefCountedPtr<HitTestResult> HitTest(const Point& point, bool include2DOn3D = true);
    void HitTest(HitTestFilterCallback* pFilterCallback, HitTestResultCallback* pResultCallback, HitTestParameters* pHitTestParameters);
    HitTestResultBehavior::Enum HitTestPoint(HitTestFilterCallback* pFilterCallback, HitTestResultCallback* pResultCallback, PointHitTestParameters* pPointParams);

    virtual HitTestResultBehavior::Enum HitTestPointInternal(HitTestFilterCallback* pFilterCallback, HitTestResultCallback* pResultCallback, PointHitTestParameters* pHitTestParameters);
 
    bool IsDescendantOf(DependencyObject* pAncestor);
    bool IsAncestorOf(DependencyObject* pDO);

    TRefCountedPtr<GeneralTransform> TransformToAncestor(Visual* pAncestor) { return InternalTransformToAncestor(pAncestor, false); }
    TRefCountedPtr<GeneralTransform> TransformToDescendant(Visual* pDescendant) { return pDescendant->InternalTransformToAncestor(this, true); }
    TRefCountedPtr<GeneralTransform2DTo3D> TransformToAncestor(Visual3D* pAncestor) { return InternalTransformToAncestor(pAncestor, false); }

    void InvalidateBbox();
    void InvalidateGeometry(bool isSubtreeInvalid);
    void InvalidateSubtreeGeometry();
    void InvalidateRect(const Rect& rect);
    static float CalculateGeometryScale(const Matrix& mat);

    unsigned short GetTreeLevel() const { return m_treeLevel; }

    bool get_HasVisualChildren() const { return ReadFlag(VisualFlags::HasChildren); }

    IMPLEMENT_EVENT(VisualAncestorChanged, AncestorChangedEventArgs);

    void ChangeVisualClip(Geometry* pNewClip, bool dontSetWhenClose);

    BitmapEffect* get_VisualBitmapEffect() { return NULL; }
    void set_VisualBitmapEffect(BitmapEffect* pValue) { }
    BitmapEffectInput* get_VisualBitmapEffectInput() { return NULL; }
    void set_VisualBitmapEffectInput(BitmapEffectInput* pValue) { }

    Rect get_VisualContentBounds();

    DoubleCollection* get_VisualXSnappingGuidelines() { return NULL; }
    DoubleCollection* get_VisualYSnappingGuidelines() { return NULL; }

    void SetFlagsToRoot(bool value, VisualFlags::Enum flag);
    DependencyObject* FindFirstAncestorWithFlagsAnd(VisualFlags::Enum flag);
    static void SetTreeBits(DependencyObject* pE, VisualFlags::Enum treeFlag, VisualFlags::Enum nodeFlag);
    static void ClearTreeBits(DependencyObject* pE, VisualFlags::Enum treeFlag, VisualFlags::Enum nodeFlag);
    static bool DoAnyChildrenHaveABitSet(Visual* pE, VisualFlags::Enum flag);

 

    bool TrySimpleTransformToAncestor(Visual* pAncestor, bool inverse, TRefCountedPtr<GeneralTransform>* pGeneralTransform, Matrix* pSimpleTransform);
    bool TrySimpleTransformToAncestor(Visual3D* pAncestor, TRefCountedPtr<GeneralTransform2DTo3D>* pGeneralTransform);

 
    TRefCountedPtr<GeneralTransform> TransformToOuterSpace();
    static void ProcessAncestorChangedNotificationRecursive(DependencyObject* e, AncestorChangedEventArgs* pArgs);

    virtual void ContentsChanged(Object* pSender, EventArgs* e);

    void PropagateChangedFlags();

protected:
    virtual void PrecomputeContent();
    virtual Rect GetHitTestBounds();
    Rect GetContentBounds();

    void AddVisualChild(Visual* pChild);
    void RemoveVisualChild(Visual* pChild, bool notifyChanged = true);
 
    virtual void OnVisualParentChanged(DependencyObject* pOldParent) { }
    virtual void OnVisualChildrenChanged(DependencyObject* pChildAdded, DependencyObject* pChildRemoved) { }

    void ReleaseRenderData();

    virtual TRefCountedPtr<HitTestResult> HitTestCore(PointHitTestParameters* pHitTestParameters);
    virtual TRefCountedPtr<GeometryHitTestResult> HitTestCore(GeometryHitTestParameters* pHitTestParameters) { LibraryCriticalError(); return NULL; };

    Rect CalculateSubgraphBoundsInnerSpace() { return CalculateSubgraphBoundsInnerSpace(false); }
    virtual Rect CalculateSubgraphBoundsInnerSpace(bool renderBounds);

    Rect CalculateSubgraphBoundsOuterSpace() { return CalculateSubgraphBoundsOuterSpace(false); }
    Rect CalculateSubgraphBoundsOuterSpace(bool renderBounds);

    Rect CalculateSubgraphRenderBoundsInnerSpace() { return CalculateSubgraphBoundsInnerSpace(true); }
    Rect CalculateSubgraphRenderBoundsOuterSpace() { return CalculateSubgraphBoundsOuterSpace(true); }

private: 
    bool Enter();
    void Exit();

    bool IsEmptyRenderBounds(const Rect& bounds);

    Visual* InternalGetVisualChild(int index) { return GetVisualChild(index); }
    virtual DependencyObject* InternalGet2DOr3DVisualChild(int index) { return GetVisualChild(index); }
    void TransformChanged(Object* pSender, EventArgs* pArgs);
    
    int get_InternalVisualChildrenCount() { return get_VisualChildrenCount(); }
    virtual int get_InternalVisual2DOr3DChildrenCount() { return get_VisualChildrenCount(); }

    virtual void FireOnVisualParentChanged(DependencyObject* pOldParent);


    void SetTreeLevel(unsigned short treeLevel);
    void ClearRenderDataClientGeometry();

    Rect ChildToParent(const Rect& rect);

    TRefCountedPtr<GeneralTransform> InternalTransformToAncestor(Visual* pAncestor, bool invertMatrix);
    TRefCountedPtr<GeneralTransform2DTo3D> InternalTransformToAncestor(Visual3D* pAncestor, bool inverse);

    void set_RenderData(RenderData* pRenderData);
    RenderData* get_RenderData() { return m_pRenderData; }

    static UncommonField<Object> VisualOpacityField;  
    static UncommonField<Transform>      VisualTransformField;  
    static UncommonField<Geometry> VisualClipField;  
    static UncommonField<Brush> OpacityMaskField;  
    static UncommonField<Effect> EffectField;  

    TWeakPtr<DependencyObject> m_pVisualParent;
    TRefCountedPtr<RenderData> m_pRenderData;
    Vector m_offset;
    Rect m_bboxSubgraph;


    void set_HasVisualTransform(bool hasVisualTransform) { WriteFlag(VisualFlags::HasVisualTransform, hasVisualTransform); }
    bool get_HasVisualTransform() const { return ReadFlag(VisualFlags::HasVisualTransform); }

    void set_HasVisualOpacity(bool hasVisualOpacity) { WriteFlag(VisualFlags::HasVisualOpacity, hasVisualOpacity); }
    bool get_HasVisualOpacity() const { return ReadFlag(VisualFlags::HasVisualOpacity); }

    void set_IsGeometryValid(bool isGeometryValid) { WriteFlag(VisualFlags::IsGeometryValid, isGeometryValid); }
    bool get_IsGeometryValid() const { return ReadFlag(VisualFlags::IsGeometryValid); }

    void set_HasOpacityMask(bool hasOpacityMask) { WriteFlag(VisualFlags::HasOpacityMask, hasOpacityMask); }
    bool get_HasOpacityMask() const { return ReadFlag(VisualFlags::HasOpacityMask); }

    void set_NodeHasEffect(bool hasEffect) { WriteFlag(VisualFlags::NodeHasEffect, hasEffect); }
    bool get_HasEffect() const { return ReadFlag(VisualFlags::NodeHasEffect); }

    void set_IsOpacitySuppressed(bool isOpacitySuppressed) { WriteFlag(VisualFlags::IsOpacitySuppressed, isOpacitySuppressed); }
    bool get_IsOpacitySuppressed() const { return ReadFlag(VisualFlags::IsOpacitySuppressed); }

    void set_IsCollapsed(bool isCollapsed) { WriteFlag(VisualFlags::IsCollapsed, isCollapsed); }
    bool get_IsCollapsed() const { return ReadFlag(VisualFlags::IsCollapsed); }

    void set_IsVisualBrushRoot(bool isVisualBrushRoot) { WriteFlag(VisualFlags::IsVisualBrushRoot, isVisualBrushRoot); }
    bool get_IsVisualBrushRoot() const { return ReadFlag(VisualFlags::IsVisualBrushRoot); }

    void set_IsVisualChildrenIterationInProgress(bool isVisualChildrenIterationInProgress) { WriteFlag(VisualFlags::IsVisualChildrenIterationInProgress, isVisualChildrenIterationInProgress); }
    bool get_IsVisualChildrenIterationInProgress() const { return ReadFlag(VisualFlags::IsVisualChildrenIterationInProgress); }

    void WriteFlag(unsigned int flag, bool value) { m_visualFlags = (unsigned int) (value ? m_visualFlags | flag : m_visualFlags & ~flag); }
    bool ReadFlag(unsigned int flag) const { return (m_visualFlags & flag) != 0; }

    bool CheckFlagsAnd(unsigned int flags)
    {
        return ((m_visualFlags & flags) == flags);
    }
    bool CheckFlagsOr(unsigned int flags)
    {
        if (flags != VisualFlags::None)
        {
            return ((m_visualFlags & flags) > VisualFlags::None);
        }
        return true;
    }

    void SetFlags(bool value, unsigned int flags)
    {
        m_visualFlags = (unsigned int) (value ? (m_visualFlags | flags) : (m_visualFlags & ~flags));
    }

    static void PropagateFlags(Visual* pVisual, VisualFlags::Enum value);
    void Precompute();
    void PrecomputeRecursive(Rect* pBboxSubgraph);

    unsigned int m_visualFlags;
    unsigned short m_treeLevel;    
};


}; // namespace AVUI
